package edu.ntu.im.p2ptrade.security;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.spec.PKCS8EncodedKeySpec;

public class KeyManager {
	private static KeyStore mykeystore;

	private static KeyManager me;

//	private static final String STORE_FILE = "p2pStore.jks";

	public static final String PASSWORD = "06121104";

	private static final String PRIVATE_KEY_ALIAS = "P2PTradePriv";

	private static final String CERT_ALIAS = "P2PTradeCert";
	
	private static final String PRIVATE_KEY_FILE = "./private/p2pPriv.der";
	public static final String PUBLIC_KEY_FILE = "./public/p2pPub.crt";
	private static final String P2PTRADE_KEY_FILE = "./public/p2pCRT.crt";
	
	private static final String KEYSTORE = "p2ptradeks.jks";

	public static void initialize() throws Exception  {
		File f = new File(KEYSTORE);
		mykeystore = KeyStore.getInstance("jks");
		if (f.exists()){
			FileInputStream in;
			try {
				in = new FileInputStream(f);
				try {
					mykeystore.load(in, PASSWORD.toCharArray());
				} catch (Exception e) {
					e.printStackTrace();
				}
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
//			TODO: see if key store exists
			try {
				if (mykeystore.containsAlias(PRIVATE_KEY_ALIAS) && mykeystore.containsAlias(CERT_ALIAS)){
					return ;
				}
			} catch (KeyStoreException e) {
				e.printStackTrace();
			}
		}else {
			try {
				mykeystore.load(null, PASSWORD.toCharArray());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		File prikf = new File(PRIVATE_KEY_FILE);
		File pubkf = new File(PUBLIC_KEY_FILE);
		File crtkf = new File(P2PTRADE_KEY_FILE);
		
		if (!crtkf.exists()){
			throw new Exception("no certificate exists! plz put ur public key by instruction!");
		}
		
		if (!prikf.exists()){
			throw new Exception("no private key exists! plz consult p2ptade admin!");
		}
		if (!pubkf.exists()){
			throw new Exception("no public key exists! plz put ur public key by instruction!");
		}
		
		importPrivateKey(PRIVATE_KEY_FILE,PUBLIC_KEY_FILE, PASSWORD);
		importCertification(P2PTRADE_KEY_FILE);
		KeyManager.getInstance();

	}

	public synchronized static KeyManager getInstance() {
		if (me == null) {
			me = new KeyManager();
		}
		return me;
	}

	private KeyManager() {
		File f = new File(KEYSTORE);
		try {
			mykeystore = KeyStore.getInstance("jks");
		} catch (KeyStoreException e1) {
			// do nothing, should not happen!
		}
		if (!f.exists()) {
			try {
				mykeystore.load(null, PASSWORD.toCharArray());
			} catch (Exception e) {
				e.printStackTrace();
			}
		} else {
			FileInputStream in;
			try {
				in = new FileInputStream(f);
				try {
					mykeystore.load(in, PASSWORD.toCharArray());
				} catch (Exception e) {
					e.printStackTrace();
				}
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
		}
		
		
	}

	// public void
	public static Certificate[] readCertification(String file) throws Exception {
		FileInputStream certificateStream = new FileInputStream(file);
		CertificateFactory certificateFactory = CertificateFactory
				.getInstance("X.509");
		java.security.cert.Certificate[] chain = {};
		chain = certificateFactory.generateCertificates(certificateStream)
				.toArray(chain);
		certificateStream.close();
		return chain;
	}

	public static PrivateKey readPrivateKey(String privKey) throws Exception {
		File keyFile = new File(privKey);
		byte[] encodedKey = new byte[(int) keyFile.length()];
		FileInputStream keyInputStream = new FileInputStream(keyFile);
		keyInputStream.read(encodedKey);
		keyInputStream.close();
		KeyFactory rSAKeyFactory = KeyFactory.getInstance("RSA");
		PrivateKey privateKey = rSAKeyFactory
				.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
		return privateKey;
	}

	public static void importPrivateKey(String privKey, String pubCert, String password)
			throws Exception {
		PrivateKey privateKey = readPrivateKey(privKey);
		Certificate[] certs = readCertification(pubCert);
		mykeystore.setEntry(PRIVATE_KEY_ALIAS,
				new KeyStore.PrivateKeyEntry(privateKey, certs),
				new KeyStore.PasswordProtection(password.toCharArray()));
		// Write out the keystore
		FileOutputStream keyStoreOutputStream = new FileOutputStream(KEYSTORE);
		mykeystore.store(keyStoreOutputStream, PASSWORD.toCharArray());
		keyStoreOutputStream.close();
		
	}

	public static void importCertification(String certFile) throws Exception {
		Certificate[] certs = readCertification(certFile);
		mykeystore.setCertificateEntry(CERT_ALIAS, certs[0]);
		FileOutputStream keyStoreOutputStream = new FileOutputStream(KEYSTORE);
		mykeystore.store(keyStoreOutputStream, PASSWORD.toCharArray());
		keyStoreOutputStream.close();
	}

	public PrivateKey getPrivateKey(String password) throws KeyStoreException,
			NoSuchAlgorithmException, UnrecoverableKeyException {
		PrivateKey pKey = (PrivateKey) this.mykeystore.getKey(
				PRIVATE_KEY_ALIAS, password.toCharArray());
		return pKey;
	}

	public Certificate getCertificate() throws KeyStoreException {
		return mykeystore.getCertificate(CERT_ALIAS);
	}

	public static void main(String args[]) throws Exception {
		KeyManager.initialize();
		if (KeyManager.getInstance().getCertificate() == null){
			System.out.println("null");
		}
		if (KeyManager.getInstance().getPrivateKey(PASSWORD) == null){
			System.out.println("null");
		}
		/*KeyManager keyMan = KeyManager.getInstance();
		keyMan.importPrivateKey("./ann/private/ann.der",
				"./ann/public/ann.crt", "06121104");
		keyMan.importCertification("./public/ca.crt");
		Certificate cert = keyMan.getCertificate();
		Certificate[] pub = KeyManager
				.readCertification("./ann/public/ann.crt");
		pub[0].verify(cert.getPublicKey());*/
		/*
		 * KeyManager keyMan = KeyManager.getInstance();
		 * keyMan.importPrivateKey("./private/p2ptrade.der",
		 * "./public/p2ptrade.crt", "06121104");
		 * keyMan.importCertification("./public/ca.crt"); Certificate cert=
		 * keyMan.getCertificate(); Certificate[] pub =
		 * KeyManager.readCertification("./public/p2ptrade.crt");
		 * pub[0].verify(cert.getPublicKey());
		 */
	}

	public  boolean verify(Certificate pubkey) {
		try {
			pubkey.verify(this.getCertificate().getPublicKey());
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	
	
	
}
